package xyz.lwm.lazycat.utility;

import xyz.lwm.lazycat.utility.exception.IoRuntimeException;

import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;

/**
 * @author lwm
 */
public abstract class IoUtil {

    /**
     * read input stream with UTF-8 charset, and close it
     *
     * @param in input stream
     * @return string
     * @throws IoRuntimeException io exception
     */
    public static String readUtf8(InputStream in) throws IoRuntimeException {
        return read(in, StandardCharsets.UTF_8);
    }

    /**
     * read input stream with charset, and close it
     *
     * @param in input stream
     * @param charset charset
     * @return string
     * @throws IoRuntimeException io exception
     */
    public static String read(InputStream in, Charset charset) throws IoRuntimeException {
        return new String(readBytes(in, true), charset);
    }

    /**
     * read input stream as bytes
     *
     * @param in input stream
     * @param isClose choose whether close input stream
     * @return bytes
     * @throws IoRuntimeException io exception
     */
    public static byte[] readBytes(InputStream in, boolean isClose) throws IoRuntimeException {
        // 文件流的长度是可预见的，此时直接读取效率更高
        final byte[] result;
        try {
            final int available = in.available();
            if (available <= 0) {
                return new byte[0];
            }
            result = new byte[available];
            final int readLength = in.read(result);
            if (readLength != available) {
                throw new IOException(StringUtil.format("File length is [{}] but read [{}]!", available, readLength));
            }
        } catch (IOException e) {
            throw new IoRuntimeException(e);
        } finally {
            if (isClose) {
                close(in);
            }
        }
        return result;
    }

    /**
     * write bytes to output stream
     *
     * @param bytes bytes
     * @param out output stream
     * @param isClose choose whether close output stream
     * @throws IoRuntimeException io exception
     */
    public static void writeBytes(byte[] bytes, OutputStream out, boolean isClose) throws IoRuntimeException {
        try {
            out.write(bytes);
        } catch (IOException e) {
            throw new IoRuntimeException(e);
        } finally {
            if (isClose) {
                close(out);
            }
        }
    }

    /**
     * copy input stream to output stream
     *
     * @param in input stream
     * @param out output stream
     * @param isClose choose whether close input and output stream
     * @throws IoRuntimeException io exception
     */
    public static void copy(InputStream in, OutputStream out, boolean isClose) throws IoRuntimeException {
        try {
            final byte[] buffer = new byte[8192];
            int readSize;
            while ((readSize = in.read(buffer)) != -1) {
                out.write(buffer, 0, readSize);
            }
        } catch (IOException e) {
            throw new IoRuntimeException(e);
        } finally {
            if (isClose) {
                close(out);
                close(in);
            }
        }
    }

    /**
     * close closeable
     *
     * @param closeable closeable
     */
    public static void close(Object closeable) {
        if (closeable instanceof Closeable) {
            try {
                ((Closeable) closeable).close();
            } catch (Exception e) {
                //
            }
        }
    }

}
